package com.gitee.wsl.struct.grammar.parser.combinator

import com.gitee.wsl.struct.grammar.parser.Parser
import com.gitee.wsl.struct.grammar.parser.ParsingScope
import com.gitee.wsl.struct.grammar.parser.parser
import com.gitee.wsl.struct.grammar.parser.context.ext.reduce
import com.gitee.wsl.struct.grammar.parser.context.ext.reduceRight
import com.gitee.wsl.struct.grammar.parser.context.ext.repeat
import com.gitee.wsl.struct.grammar.parser.context.ext.split

/**
 * Returns the result of this parser executing [left] and [right] parsers before and after it respectively.
 */
fun <T> Parser<T>.between(left: Parser<*>, right: Parser<*>): Parser<T> = Parser.parser(left.firstTokens) {
    left()
    val result = this@between()
    right()
    result
}

fun <T : Any> Parser<T>.repeated(atLeast: Int, atMost: Int = -1): Parser<List<T>> {
    return Parser.parser(if (atLeast > 0) firstTokens else emptySet()) {
        repeat(this@repeated, atLeast, atMost)
    }
}

infix fun <T : Any> Parser<T>.times(count:Int ): Parser<List<T>> = repeated( atLeast = count, atMost = count)

infix fun <T : Any> Parser<T>.timesOrMore(count:Int): Parser<List<T>> = repeated( atLeast = count)

infix fun <T : Any>  Parser<T>.times(count:IntRange): Parser<List<T>> = repeated( atLeast = count.first, atMost = count.last)

infix fun <T : Any> Int.times(parser: Parser<T>): Parser<List<T>> = parser.repeated( atLeast = this, atMost = this)

infix fun <T : Any> IntRange.times(parser: Parser<T>): Parser<List<T>> = parser.repeated( atLeast = first, atMost = last)

infix fun <T : Any> Int.timesOrMore(parser: Parser<T>): Parser<List<T>> = parser.repeated( atLeast = this)

fun <T : Any> ParsingScope.repeated(p: Parser<T>, atLeast: Int, atMost: Int = -1): Parser<List<T>> =  p.repeated(atLeast, atMost)

fun <T : Any> ParsingScope.zeroOrMore(p: Parser<T>) = repeated(p, atLeast = 0)

fun <T : Any> ParsingScope.oneOrMore(p: Parser<T>) = repeated(p, atLeast = 1)

fun <T : Any> Parser<T>.zeroOrMore() = repeated(atLeast = 0)

fun <T : Any> Parser<T>.oneOrMore() = repeated(atLeast = 1)

fun <T : Any, S : Any> Parser<T>.separated(
       separator: Parser<S>,
       allowEmpty: Boolean = true,
       trailingSeparator: Boolean = false,
       ): Parser<List<T>>{
    return Parser.parser(if (!allowEmpty) firstTokens else emptySet()) {
        split(this@separated,separator, allowEmpty, trailingSeparator)
    }
}

inline fun <T : Any, S : Any> Parser<T>.leftAssociative(
    separator: Parser<S>,
    crossinline transform: (T, S, T) -> T
): Parser<T> = Parser.parser(firstTokens) {
    reduce(this@leftAssociative, separator, transform)
}

inline fun <T : Any, S : Any> Parser<T>.leftAssociative(
    separator: Parser<S>,
    crossinline transform: (T, T) -> T
): Parser<T> = leftAssociative(separator) { a, _, b -> transform(a, b) }


inline fun <T : Any, S : Any> Parser<T>.rightAssociative(
    separator: Parser<S>,
    crossinline transform: (T, S, T) -> T
): Parser<T> =
    Parser.parser(firstTokens) {
        reduceRight(this@rightAssociative, separator, transform)
    }

inline fun <T : Any, S : Any> Parser<T>.rightAssociative(
    separator: Parser<S>,
    crossinline transform: (T, T) -> T
): Parser<T> = rightAssociative(separator) { a, _, b -> transform(a, b) }


